<template>
  <div class=" relative " >
      <div class="messages overflow-y-auto" :style="{height:height+'px'}">
          <div class="w-800px mx-auto" >
              <div v-for="(message, index) in messages"  :key="index" :class="['message', message.role]" >
                  
                  <div  v-if="message.streaming" class="streaming-content inline-block max-w-550px">
                   

                    <div class="content ">
                      <div v-if="streamingText.knowledge" class="flex mb-20px">
                        <div v-for="(item,index) in streamingText.knowledge" class="mr-10px">
                          <el-card shadow="hover"> 
                            <div class="text-12px linkText"><a :href="item.meta.link.url">{{ item.meta.card.title }}</a></div>
                            <!-- <div class="text-12px text-gray-500">{{ item.meta.card.con }}</div> -->
                          </el-card>
                        </div>
                      </div>
                      <div v-if="streamingText.content == ''" class="loading text-14px text-gray-500 my-10px">思考中，请稍后...</div>
                      <div v-if="streamingText.reason" class="text-12px text-gray-500 border-l-4px border-l-gray-100 px-10px leading-24px mb-20px" v-html="renderMarkdown(streamingText.reason)"></div>
                      <div v-if="streamingText.content" v-html="renderMarkdown(streamingText.content)"></div>
                    </div>
                    <div class="mt-20px" v-if="streamingText.follow_up">
                      <div  v-for="(item,index) in streamingText.follow_up" :key="index">
                        <span class="p-10px bg-gray-200 rounded-10px m-5px inline-block text-14px text-dark-300">{{ item }}</span>
                      </div>
                    </div>
                    

                  </div>
                  <div  v-else class="max-w-550px inline-block" >
                    <div class="content ">
                      <div v-if="message.content.knowledge" class="flex mb-20px">
                        <div v-for="(item,index) in message.content.knowledge" class="mr-10px">
                          <el-card shadow="hover"> 
                            <div class="text-12px linkText"><a :href="item.meta.link.url" v-if="item.meta.link.url">{{ item.meta.card.title }}</a><a href="javascript:;" v-else>{{ item.meta.card.title }}</a></div>
                            <!-- <div class="text-12px text-gray-500">{{ item.meta.card.con }}</div> -->
                          </el-card>
                        </div>
                      </div>
                      <div v-if="message.content.reason" class="text-12px text-gray-500 border-l-4px border-l-gray-100 px-10px leading-24px mb-20px" v-html="renderMarkdown(message.content.reason)"></div>
                      <div v-if="message.content.content" v-html="renderMarkdown(message.content.content)"></div>
                    </div>
                    <div class="mt-20px" v-if="message.content.follow_up">
                      <div  v-for="(item,index) in message.content.follow_up" :key="index">
                        <span class="p-10px bg-gray-200 rounded-10px m-5px inline-block linkText text-14px text-dark-300" @click="sendMessageAuto(item)">{{ item }}</span>
                      </div>
                    </div>
                  </div>
              </div>
              
              <div class="h-100px"></div>
          </div>
      </div>


      <div class="w-800px mx-auto">
        <div class="w-800px h-100px rounded-10px bg-white h-60px fixed bottom-130px shadow-lg text-16px">
          <div class="">
            <el-input v-model="inputText" :resize="none" class="border-0 no-border-input rounded-20px" type="textarea" :rows="2" @keyup.enter.exact.prevent="sendMessage" placeholder="请输入您的信息..."></el-input>
            <!-- <el-input v-model="inputText" class="h-80px" type="input" rows="3" @keyup.enter.exact.prevent="sendMessage" placeholder="请输入您的信息...">
              <template #append>
                <el-icon  @click="sendMessage" class="w-100px" :size="30"  :disabled="loading">
                  <block class="text-16px" v-if="loading">
                    发送中...
                  </block>
                  <block else>
                    <Promotion />
                  </block>
                </el-icon>
              </template>
            </el-input> -->
              <!-- <textarea v-model="inputText"  @keyup.enter.exact.prevent="sendMessage" placeholder="请输入您的信息..." ></textarea>
              <button @click="sendMessage" :disabled="loading"> {{ loading ? '输出中...' : '发送' }} </button> -->
          </div>
          <div class="flex justify-end p-10px">
            <el-button type="info" :icon="Delete" circle @click="clearMessage" :disabled="loading"/>
            <el-button type="danger" :icon="Position" circle @click="sendMessage" :disabled="loading"></el-button>
          </div>
        </div>
      </div>
      
  </div>
    
  
    
    
  
</template>

<script setup>
import axios from 'axios'
import {Check,Delete,Edit,Message,Search,Star,Position} from '@element-plus/icons-vue'
import { ElMessage,ElMessageBox } from 'element-plus'
// 新增依赖引入
import { ref, onMounted, onUnmounted,nextTick,onBeforeUnmount  } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { initMarkdown, safeMarkdown } from '../../utils/markdown'
import { useUserStore } from '../../stores/user'
import { configStore } from '../../stores/config'
import { httpApi } from '../../api/api'
import { decryptString } from '../../utils/tool'
import CryptoJS from 'crypto-js'

const router = useRouter()
const renderMarkdown = (content) => {
return safeMarkdown(content)
}

const height = ref(0); // 获取浏览器窗口的高度
const updateHeight = () => {
height.value = window.innerHeight -250;
console.log(height.value)
}

// 响应式数据
const userStore = useUserStore()
const config = configStore()
const messages = ref([])
const inputText = ref('')
const loading = ref(false)
const userInfo = ref(userStore.getUserInfo())
const configData = ref({})
const aiSetting = ref({})
const streamingText = ref({
knowledge:[],
reason:"",
content:"",
follow_up:[]

})

//获取AI设置
const getAiSetting = async () => {
    let res =await httpApi.getAiSetting()
    //console.log("AI设置",res)
    if(res.code){
      aiSetting.value.baseUrl = res.data.baseUrl
      aiSetting.value.api_key = decryptString(res.data.api_key)
      // console.log("AI设置",res)
      // console.log("AI配置数据",aiSetting.value)
    }
  }

const controller = ref(null)



//自动发消息
const sendMessageAuto = async (text) => {
  if(text != ""){
    inputText.value = text
    sendMessage()
  
  }
}
// 发送消息
const sendMessage = async () => {

if (!inputText.value.trim() || loading.value) return

try {
  // 添加用户消息
  messages.value.push({
    role: 'user',
    content: {
      content:inputText.value

    },
    streaming: false
  })

  // 添加 AI 占位消息
  const aiMessage = {
    role: 'assistant',
    content: {
      knowledge:[],
      reason:"",
      content:"",
      follow_up:[]
    },
    streaming: true
  }
  messages.value.push(aiMessage)
  
  const userMessage = inputText.value
  inputText.value = ''
  loading.value = true
  streamingText.value = {
    knowledge:[],
    reason:"",
    content:"",
    follow_up:[]
  } //流式内容清空
  let messageComplete = true //流式内容是否完成
  
  // 创建 AbortController
  controller.value = new AbortController()
 // console.log("api链接",aiSetting.value.baseUrl);
 const currentToken = userStore.getCozeToken()
 
 configData.value = config.getBook()
 console.log("当前机器人",configData.value.chat_id)
// 发起流式请求
const response = await fetch(aiSetting.value.baseUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${aiSetting.value.api_key}`,
    },
    body:JSON.stringify({
      "bot_id": configData.value.chat_id,
      "user_id": userInfo.value.mobile,
      "stream": true,
      "auto_save_history":true,
      "additional_messages":[
      ...messages.value
        .filter(m => !m.streaming)
        .map(m => ({ role: m.role, content: m.content.content })),
      { role: 'user', content: userMessage }
    ],
    }),
    // body: JSON.stringify({
    //   messages: [
        
    //   ],
    //   model: 'deepseek-chat',
    //   stream: true,
    //   temperature: 0.7
    // }),
    signal: controller.value.signal
  })

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Coze API 错误: ${error}`);
  }

  // 处理流式数据
  const reader = response.body.getReader()
  const decoder = new TextDecoder()
  let buffer = '';
  
  while (true) {
    const { done, value } = await reader.read()
    
    if (done) break
    buffer += decoder.decode(value, { stream: true });
    
    let lineEnd;
    while ((lineEnd = buffer.indexOf('\n')) !== -1) {
      const line = buffer.slice(0, lineEnd).trim();
      buffer = buffer.slice(lineEnd + 1);

      if (line.startsWith('data:')) {
        try {
          const jsonData = JSON.parse(line.slice(5).trim());
          //console.log('收到回答内容:', jsonData);
          // 处理消息内容
          if (jsonData.content || jsonData.reasoning_content) {
            //console.log('收到内容:', jsonData.content);
            if(jsonData.type == 'verbose'){  //处理知识库返回
              let verbose = JSON.parse(jsonData.content)
              if(verbose.msg_type == "knowledge_recall"){
                let knowledge = JSON.parse(verbose.data)
                streamingText.value.knowledge = knowledge.chunks
              }
             // console.log('收到冗长内容:', JSON.parse(JSON.parse(jsonData.content).data));
            }
            if(jsonData.type == 'answer' && jsonData.content_type == 'text'){
              
              if(jsonData.reasoning_content){
                //console.log("reasoning_content",jsonData.reasoning_content)
                streamingText.value.reason += jsonData.reasoning_content
              }else{
               // console.log(messageComplete)
                if(!messageComplete){
                  streamingText.value.content += jsonData.content
                }else{
                  streamingText.value.content = jsonData.content
                }
                
              }
            }
            if(jsonData.type == "follow_up"){ //后边推荐的内容
              streamingText.value.follow_up.push(jsonData.content)
            }

            await nextTick()
            scrollToBottom()
            // 更新到 Vue 的响应式变量（示例）
            //streamingText.value += jsonData.content;
            //console.log("接收的数据",streamingText.value)
          }
        } catch (err) {
          console.error('解析 data 失败:', err, '原始行:', line);
        }
      } else if (line.startsWith('event:')) {
        const eventType = line.slice(6).trim();
        if(eventType == 'conversation.message.delta'){
          messageComplete = false
        }else{
          messageComplete = true
        }
       // console.log("查看表头",eventType)
        if (eventType === 'done') {
         
          console.log('对话结束');
        }
      }
    }

   


  }

  // 完成流式处理
  messages.value = messages.value.map(msg => {
    if (msg.streaming) {
      return {
        ...msg,
        content: streamingText.value,
        streaming: false
      }
    }
    return msg
  })

  userStore.setMessages(messages.value)
  
} catch (error) {
  if (error.name !== 'AbortError') {
    console.error('Stream error:', error)
    messages.value.push({
      role: 'system',
      content: 'Error: Connection interrupted'
    })
  }
} finally {
  loading.value = false
  streamingText.value = {
    content: '',
    reasoning_content: '',
    knowledge: [],
    follow_up: []
  
  }
  controller.value = null
}
}


// 自动滚动
const scrollToBottom = () => {
const container = document.querySelector('.messages')
if (container) {
  //console.log("滚动到底部",container.scrollTop)
  container.scrollTop = container.scrollHeight
}
}

const clearMessage = ()=>{

ElMessageBox.confirm(
    '清除所有消息？',
    '警告',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    }
  )
    .then(() => {
      messages.value = []
      userStore.setMessages([])
      
      ElMessage({
        type: 'success',
        message: '清除成功',
      })
    })
    .catch(() => {
      
    })
}



// 停止生成
const stopGenerating = () => {
if (controller.value) {
  controller.value.abort()
  loading.value = false
}
}

//开始设置扣子token的内容
const cozeTokenData = ref({});

// 配置参数（需要替换为coze.cn的官方配置）
const configCoze = {
  clientId: '59913591726407385069430754498653.app.coze',
  authorizationEndpoint: 'https://www.coze.cn/api/permission/oauth2/authorize',
  tokenEndpoint: 'https://api.coze.cn/api/permission/oauth2/token',
  redirectUri: 'https://zhishekao.sgkspx.cn/ai/callback', // 必须和注册的redirect_uri一致
  state: 'zhishekao' // 根据实际需求修改
}

// Token 管理相关变量
let refreshTimeout = null
let isRefreshing = false
let failedQueue = []

// 生成 PKCE 相关参数
const generatePKCE = async () => {
  const codeVerifier = CryptoJS.lib.WordArray.random(64).toString(CryptoJS.enc.Base64)
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '')
  
  const codeChallenge = CryptoJS.SHA256(codeVerifier)
    .toString(CryptoJS.enc.Base64)
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '')
  
  return { codeVerifier, codeChallenge }
}
// Token 存储管理
const storeTokens = (response) => {
  const { access_token, refresh_token, expires_in } = response
  const expiresAt = Date.now() + (expires_in * 1000)
  
  userStore.setCozeToken(response);
  
  setupTokenRefresh(expires_in)
}
// 设置自动刷新定时器
const setupTokenRefresh = (expiresIn) => {
  // 清除现有定时器
  // if (refreshTimeout) clearTimeout(refreshTimeout)
  
  // 在过期前5分钟刷新
  const refreshBuffer = 60000 // 1分钟
  const delay = expiresIn * 1000 - refreshBuffer
  //console.log("设置定时器",delay)
  refreshTimeout = setTimeout(() => {
    refreshToken()
  },delay)
}
// Token 刷新逻辑
const refreshToken = async () => {
  if (isRefreshing) {
    return
    // return new Promise((resolve) => {
    //   failedQueue.push(resolve)
    // })
  }
  //console.log("刷新token")
  isRefreshing = true
  

  try {
    const response = await axios.post(configCoze.tokenEndpoint, JSON.stringify({
      client_id: configCoze.clientId,
      grant_type: 'refresh_token',
      refresh_token: cozeTokenData.value.refresh_token
    }), {
      headers: {
        'Content-Type': 'application/json'
      }
    })

    storeTokens(response.data)
    // 重试队列中的请求
    //failedQueue.forEach(promise => promise())
    //failedQueue = []
    
    // return Promise.resolve(response.data.access_token)
  } catch (error) {
    // console.error('Token refresh failed:', error)
    // clearAuth()
    // //router.push('/login')
    // return Promise.reject(error)
  } finally {
    isRefreshing = false
  }
}
// 初始化授权流程
const initiateOAuthFlow = async () => {
  const { codeVerifier, codeChallenge } = await generatePKCE()
  
  // 存储 codeVerifier 用于后续交换token
  sessionStorage.setItem('code_verifier', codeVerifier)
  
  // 构建授权URL
  const authUrl = new URL(configCoze.authorizationEndpoint)
  authUrl.searchParams.append('client_id', configCoze.clientId)
  authUrl.searchParams.append('redirect_uri', configCoze.redirectUri)
  authUrl.searchParams.append('response_type', 'code')
  authUrl.searchParams.append('state', configCoze.state)
  authUrl.searchParams.append('code_challenge', codeChallenge)
  authUrl.searchParams.append('code_challenge_method', 'S256')
  
  // 跳转到授权页面
  window.location.href = authUrl.toString()
}

// 清除认证信息
const clearAuth = () => {
  // userStore.setCozeToken({})
  // sessionStorage.removeItem('code_verifier')
  if (refreshTimeout) clearTimeout(refreshTimeout)
}

// 检查 Token 是否有效
const isTokenValid = () => {
  
  const expiresAt = cozeTokenData.value.expires_in
  //console.log("检测token是否过期",Date.now(),"||",expiresAt*1000)
  return Date.now() > parseInt(expiresAt*1000 || 0)
}





// 处理回调
const handleCallback = async () => {
  const route = useRoute()
  
  // 检查错误
  if (route.query.error) {
    console.error('Authorization failed:', route.query.error)
    return
  }
  
  // 获取授权码
  const code = route.query.code
  if (!code) return

  try {
    // 获取存储的 code_verifier
    const codeVerifier = sessionStorage.getItem('code_verifier')
    
    // 交换token
    const response = await fetch(configCoze.tokenEndpoint, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: new URLSearchParams({
        client_id: configCoze.clientId,
        code,
        redirect_uri: configCoze.redirectUri,
        grant_type: 'authorization_code',
        code_verifier: codeVerifier
      })
    })

    const tokenData = await response.json()
    
    // 存储token（根据实际需求调整存储方式）
    //userStore.setCozeToken(tokenData);
    storeTokens(tokenData) //该方法会自动启动定时刷新
    //sessionStorage.setItem('access_token', tokenData.access_token)
    sessionStorage.removeItem('code_verifier')
    router.push({path:"/ai"})
    //console.log('OAuth flow completed successfully')
    
  } catch (error) {
    console.error('Token exchange failed:', error)
  }
}



onMounted(async() => {
  //cozeTokenData.value = userStore.getCozeToken()
  // if (window.location.pathname === '/ai/callback') { //回调使用
  //   handleCallback()
  // }else{ //初始授权使用
  //   if(!cozeTokenData.value.access_token){
  //     //initiateOAuthFlow() //这个是扣子的auth2.0授权认证，现在不用了
  //   }
    
  // }

  // 页面可见性变化时检查 Token
  // document.addEventListener('visibilitychange', () => {
  //   console.log("显示页面")
  //   cozeTokenData.value = userStore.getCozeToken()
  //   refreshToken()
  //   if (document.visibilityState === 'visible' && !isTokenValid()) {
  //     console.log("显示页面2")
  //     refreshToken()
  //   }
  // })

  initMarkdown()
  updateHeight();
  getAiSetting();
  window.addEventListener('resize', updateHeight);
  //userStore.setMessages([])
  messages.value = userStore.getMessages() //加载的时候把现有的聊天记录加载进来
  await nextTick()
  scrollToBottom()
})
onUnmounted(()=>{
//userStore.setMessages(messages.value) //退出时把消息存入本地
})
onBeforeUnmount(() => {
  document.removeEventListener('visibilitychange', () => {})
})

</script>

<style scoped>
/* 新增流式样式 */
::v-deep .el-footer{display: none!important;}
.streaming-content {
position: relative;
}

.streaming-content::after {
content: "▌";
animation: blink 1s infinite;
color: #666;
margin-left: 2px;
}

@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}

/* 优化滚动体验 */
.messages {
scroll-behavior: smooth;
}
/* 新增 Markdown 样式 */
.content >>> pre {
background: #f6f8fa;
padding: 1em;
border-radius: 6px;
overflow-x: auto;
margin: 1em 0;
}

.content >>> code {
font-family: 'Fira Code', monospace;
background: rgba(175, 184, 193, 0.2);
padding: 0.2em 0.4em;
border-radius: 3px;
}

.content >>> blockquote {
border-left: 4px solid #dfe2e5;
color: #6a737d;
margin: 0;
padding: 0 1em;
}

.content >>> table {
border-collapse: collapse;
margin: 1em 0;
}

.content >>> td, .content >>> th {
border: 1px solid #dfe2e5;
padding: 0.5em 1em;
}

.content >>> a {
color: #0366d6;
text-decoration: none;
}

.content >>> a:hover {
text-decoration: underline;
}

.user .content >>> pre {
background: rgba(255, 255, 255, 0.15);
}
.chat-container {
  max-width: 800px;
  margin: 0 auto;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.messages {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  background: #f5f5f5;
}

.message {
  margin: 10px 0;
  max-width: 80%;
}

.message.user {
  margin-left: auto;
  text-align: right;

}

.message.assistant {
  margin-right: auto;
  text-align: left;
}

.content {
  display: inline-block;
  padding: 10px 15px;
  border-radius: 8px;
  background: white;
  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

.user .content {
  background: #F3F3F3;
  color: #303030;
}

.input-area {
  padding: 20px;
 
  
  display: flex;
  gap: 10px;
}

textarea {
  flex: 1;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
  resize: none;
  height: 80px;
}



.linkText{cursor: pointer;}

.loading {
  color: #666;
  padding: 10px;
  text-align: center;
}

.no-border-input :deep(.el-textarea__inner:hover),
.no-border-input :deep(.el-textarea__inner:focus),
.no-border-input :deep(.el-textarea__inner),
.no-border-input :deep(.el-textarea__inner:focus) {
  border: none;
  box-shadow: none;
}
</style>